package org.eclipse.epsilon.emc.jdt;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.epsilon.eol.exceptions.EolRuntimeException;
import org.eclipse.epsilon.eol.exceptions.models.EolEnumerationValueNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelElementTypeNotFoundException;
import org.eclipse.epsilon.eol.exceptions.models.EolModelLoadingException;
import org.eclipse.epsilon.eol.exceptions.models.EolNotInstantiableModelElementTypeException;
import org.eclipse.epsilon.eol.execute.introspection.IPropertyGetter;
import org.eclipse.epsilon.eol.execute.introspection.IPropertySetter;
import org.eclipse.epsilon.eol.models.Model;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclaration;

public class JdtModel extends Model {

	protected List<String> supportedTypes = Arrays.asList("JavaProject",
			"Package", "JavaFile", "Class", "Method", "Field");

	@Override
	public void load() throws EolModelLoadingException {
		
	}

	@Override
	public Object getEnumerationValue(String enumeration, String label)
			throws EolEnumerationValueNotFoundException {
		return null;
	}

	@Override
	public Collection<?> allContents() {
		try {
			return getAllOfType("JavaProjects");
		} catch (EolModelElementTypeNotFoundException e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public Collection<?> getAllOfType(String type)
			throws EolModelElementTypeNotFoundException {
		//get all java projects
		if (type.equals("JavaProject")) {
			//get all projects in the workspace including non-Java projects
			IProject [] projects = JdtManipulator.getIProjects();
			List <IJavaProject> javaProjects = new ArrayList<IJavaProject>();
			try {
				//get all java projects
				javaProjects.addAll(Arrays.asList(JdtManipulator.getIJavaProjects(projects)));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return javaProjects;
		}
		// get all packages
		if (type.equals("Package")) {
			// get all projects in the workspace including non-Java projects
			IProject[] projects = JdtManipulator.getIProjects();
			List<IPackageFragment> packages = new ArrayList<IPackageFragment>();
			try {
				// get all java projects
				IJavaProject[] javaProjects = JdtManipulator
						.getIJavaProjects(projects);
				// get all packages
				packages.addAll(Arrays.asList(JdtManipulator
						.getIPackageFragments(javaProjects, true)));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return packages;
		}
		
		//get all java files
		if (type.equals("JavaFile")){
			IProject[] projects = JdtManipulator.getIProjects();
			List<CompilationUnit> units = new ArrayList<CompilationUnit>();
			try {
				// get all java projects
				IJavaProject[] javaProjects = JdtManipulator
						.getIJavaProjects(projects);
				// get all compilations units (java files)
				units.addAll(JdtManipulator.getCompilationUnits(javaProjects));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return units;
		}
		
		//get all classes
		if(type.equals("Class")){
			IProject[] projects = JdtManipulator.getIProjects();
			List<TypeDeclaration> classes = new ArrayList<TypeDeclaration>();
			try {
				// get all java projects
				IJavaProject[] javaProjects = JdtManipulator
						.getIJavaProjects(projects);
				// get all classes
				classes.addAll(JdtManipulator
						.getTypeDeclarations(javaProjects));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return classes;
		}
		//get all methods
		if(type.equals("Method")){
			IProject[] projects = JdtManipulator.getIProjects();
			List<MethodDeclaration> methods = new ArrayList<MethodDeclaration>();
			try {
				// get all java projects
				IJavaProject[] javaProjects = JdtManipulator
						.getIJavaProjects(projects);
				// get all methods
				methods.addAll(JdtManipulator
						.getMethodDeclarations(javaProjects));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return methods;
		}
		
		//get all Fields
		if(type.equals("Field")){
			IProject[] projects = JdtManipulator.getIProjects();
			List<FieldDeclaration> fields = new ArrayList<FieldDeclaration>();
			try {
				// get all java projects
				IJavaProject[] javaProjects = JdtManipulator
						.getIJavaProjects(projects);
				// get all fields
				fields.addAll(JdtManipulator.getFieldDeclarations(javaProjects));
			} catch (CoreException e) {
				e.printStackTrace();
			}
			return fields;
		}
		return null;
	}

	@Override
	public Collection<?> getAllOfKind(String type)
			throws EolModelElementTypeNotFoundException {
		return getAllOfType(type);
	}

	@Override
	public String getTypeNameOf(Object instance) {
		if(instance instanceof IProject){
			return "IProject";
		}
		return null;
	}

	@Override
	public boolean isOfType(Object instance, String metaClass)
			throws EolModelElementTypeNotFoundException {
		return instance.getClass().getSimpleName().equals(metaClass);
	}
	
	@Override
	public Object createInstance(String type)
			throws EolModelElementTypeNotFoundException,
			EolNotInstantiableModelElementTypeException {

		return null;
	}

	@Override
	public Object getElementById(String id) {

		return null;
	}

	@Override
	public String getElementId(Object instance) {

		return null;
	}

	@Override
	public void setElementId(Object instance, String newId) {


	}

	@Override
	public void deleteElement(Object instance) throws EolRuntimeException {

	}

	@Override
	public boolean owns(Object instance) {
		if (instance instanceof IJavaElement || instance instanceof ASTNode) {
			return true;
		}
		return false;
	}

	@Override
	public boolean isInstantiable(String type){
		return true;
	}

	@Override
	public boolean hasType(String type) {
		try {
			return supportedTypes.contains(type) || (Class.forName("org.eclipse.jdt.core.dom." + type) != null);
		} catch (ClassNotFoundException e) {
			return false;
		}
	}

	@Override
	public boolean store(String location) {
		return false;
	}

	@Override
	public boolean store() {
		return false;
	}

	@Override
	public IPropertySetter getPropertySetter() {
		return new JdtPropertySetter();
	}

	@Override
	public IPropertyGetter getPropertyGetter() {
		return new JdtPropertyGetter();
	}
}
